package org.example;

import javax.servlet.Filter;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Servlet容器初始化器
 * 在Servlet容器初始化时，会调用其onStartup方法，我们的MVC框架就是在这个触发点进行初始化并运行的。
 *
 * @author hrz
 * @since 2021/3/2 0002
 */
@HandlesTypes({WebMvcApplicationInitializer.class})
public class WebMvcServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> initializers, ServletContext servletContext) throws ServletException {

        List<Class<?>> initializerClasses = initializers.stream()
                .filter(clazz -> !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) &&
                        WebMvcApplicationInitializer.class.isAssignableFrom(clazz))
                .collect(Collectors.toList());

        // 不允许有两个 WebMvcApplicationInitializer 实例
        if (initializerClasses.size() > 1) {
            throw new ServletException("存在两个[WebMvcApplicationInitializer]实例："+ Arrays.toString(initializers.toArray()));
        }

        if (initializerClasses.isEmpty()) {
            servletContext.log("至少需要有一个[WebMvcApplicationInitializer]实例！！");
        }

        try {
            Constructor<?> ctor = initializerClasses.get(0).getDeclaredConstructor();
            if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) {
                ctor.setAccessible(true);
            }
            WebMvcApplicationInitializer newInstance = (WebMvcApplicationInitializer) ctor.newInstance();
            newInstance.onStartup(servletContext);
        } catch (Throwable t) {
            throw new ServletException("Failed to instantiate WebMvcApplicationInitializer class", t);
        }
    }


}
